class Player {
  constructor(name) {
    this.name = name;
    this.operation = 0;
    this.target = "";
    this.target_ammo = 0;
    this.att_ammo = 0;
    this.att_player = [];
    this.die = false;
    this.ammo = 0;
  }
  addIn(players, turn) {
    this.target_ammo = 0;
    this.att_ammo = 0;
    this.att_player = [];
  }
  lost() {
    this.die = true;
  }
}

class Self extends Player {
  constructor(name) {
    super(name);
    this.shotting = false;
  }
  setOpeAmmo() {
    this.shotting = false;
    this.operation = 0;
    this.target = "";
    this.ammo++;
  }
  setOpeDefe() {
    this.shotting = false;
    this.operation = 1;
    this.target = "";
  }
  setAtt(num) {
    this.shotting = true;
    this.operation = num;
  }
  setOpeAtt(target) {
    this.shotting = false;
    this.target = target;
    this.ammo = this.ammo +1 -this.operation;
  }
}

class Machine extends Player {
  constructor(name) {
    super(name);
  }
  addIn(players, turn) {
    this.target = "";
    this.target_ammo = 0;
    this.att_ammo = 0;
    this.att_player = [];
    if (turn === 1){
      this.operation = 0;
    } else {
      const choices = [0, 1];
      if (this.ammo >= 1) {
        choices.push(2);
      }
      if (this.ammo >= 2) {
        choices.push(3);
      }
      if (this.ammo >= 3) {
        choices.push(4);
      }
      this.operation = choices[Math.floor(Math.random()*(choices.length))];
    }
    if (this.operation === 0) {
      this.ammo++;
    } else if (this.operation !== 1) {
      do {
        this.target = (players[Math.floor(Math.random()*(players.length))]).name;
      } while (this.target === this.name);
      if (this.operation === 2) {
        this.ammo--;
      } else if (this.operation === 3) {
        this.ammo = this.ammo -2;
      } else if (this.operation === 4) {
        this.ammo = this.ammo -3;
      }
    }
  }
}

class Game {
  constructor(number, self) {
    this.turns = 1;
    this.number = number;
    this.players = [];
    this.result = new Map();
    this.n2p = new Map();
    this.chart = new Map([['1,1',3],['1,2',1],['2,1',2],['2,2',3]]);
    this.addPlayer(self);
  }
  addPlayer(self) {
    this.players.push(self);
    this.n2p.set(self.name, self);
    this.result.set(self.name, [-1, "", 1]);
    for (let index=1; index<=this.number; index++) {
      const player = new Machine(index.toString());
      this.players.push(player);
      this.n2p.set(player.name, player);
      this.result.set(player.name, [-1, "", 1]);
    }
  }

  static lost(players) {
    players.forEach(player => player.lost());
  }

  nextTurn(self) {
    this.players.forEach(player => {
      player.addIn(this.players, this.turns);
      this.result.set(player.name, [player.operation, player.target]);
    });
    this.players.forEach(attacker => {
      if (typeof this.n2p.get(attacker.target) !== 'undefined') {
        const target = this.n2p.get(attacker.target);
        target.att_ammo += attacker.operation -1;
        target.att_player.push(attacker);
        if (target.target === attacker.name && target.name !== attacker.name) {
          target.target_ammo += target.operation -1;
        }
      }
    });
    let ammoCache = [];
    this.players.forEach(i => {
      ammoCache.push([i.att_ammo, i]);
    });
    ammoCache.forEach(cache => {
      if (cache[0] >= 3) {
        if (cache[1].operation !== 4){
          cache[1].att_player.forEach(p => {
            if (cache[1].target === p.name){
              p.att_ammo = p.att_ammo - cache[1].operation +1;
            }
          });
        }
        cache[1].lost();
      }
    });
    this.players.forEach(player => { 
      if (player.att_ammo > 1 || (player.att_ammo === 1 && player.operation !== 1)) {
        if (player.target_ammo === 0) player.lost();
        if (player.operation === 1) player.att_ammo -= 1;
        const resultKey = `${player.target_ammo},${player.att_ammo}`;
        const result = this.chart.get(resultKey);
        if (result === 1) {
          Game.lost(player.att_player);
        } else if (result === 2) {
          player.lost();
        } else if (result === 3) {
          Game.lost(player.att_player);
          player.lost();
        }
      }
    });
    this.players = this.players.filter(player => !player.die);
    const statu = this.players.length < 2, finalResult = new Map();
    this.result.forEach((value, key)=> {
      const playerExists = this.n2p.has(key) ? (!this.n2p.get(key).die ? 1 : 0) : 0;
      finalResult.set(key, [...value, playerExists]);
    });
    this.result.clear();
    this.turns++;
    return [finalResult, statu];
  }
}

let buttons = document.getElementById("d-b"),
    profils = document.getElementById("d-i"),
    hidbuts = document.getElementById("d-a"),
    openOpe = document.getElementById("i-c"),
    addAmmo = document.getElementById("b-z"),
    defense = document.getElementById("b-f"),
    attack = document.getElementById("b-k"),
    attack1 = document.getElementById("b-d"),
    attack2 = document.getElementById("b-s"),
    attack3 = document.getElementById("b-h"),
    continu = document.getElementById("con"),
    canva = document.getElementById("c-m"),
    imgYou = document.getElementById("i-y"),
    imgMac = document.getElementById("i-m");
openOpe.checked = true;
let searchParams = new URLSearchParams(location.search);
let number = parseInt(searchParams.get("number")),
    self = new Self("you");
number = isNaN(number) ? 2 : (number>10 ? 10 : (number<1 ? 1 : number));
let game = new Game(number, self);
let result = game.result;
if (searchParams.get("theme") === "dark"){
  document.documentElement.setAttribute("data-theme", "dark");
}
function turnDone(){
  [...buttons.children].forEach(child => {hidbuts.appendChild(buttons.removeChild(child));});
  [result, statu] = game.nextTurn(self);
  if ((result.get("you"))[2] === 0 || statu){
    buttons.appendChild(continu);
    document.addEventListener("mousedown", () => {
      if ((result.get("you"))[2] === 0){
        location.assign("./lost/lost.html");
      } else if (statu){
        location.assign("./win/win.html");
      }
    });
  } else {
    buttons.appendChild(hidbuts.removeChild(addAmmo));
    buttons.appendChild(hidbuts.removeChild(defense));
    if (self.ammo > 0){
      buttons.appendChild(hidbuts.removeChild(attack1));
    }
    if (self.ammo > 1){
      buttons.appendChild(hidbuts.removeChild(attack2));
    }
    if (self.ammo > 2){
      buttons.appendChild(hidbuts.removeChild(attack3));
    }
  }
};

canva.width = innerWidth*1.0;
canva.height = innerHeight - buttons.clientHeight*2;
profils.style.height = `${innerHeight - buttons.clientHeight*2}px`;
let r = Math.min(canva.width,canva.height)/3, pos = new Map(), mx, my,
    leftYou = parseInt(imgYou.style.left.substring(0,imgYou.style.left.length -2)),
    topYou = parseInt(imgYou.style.top.substring(0,imgYou.style.top.length -2)),
    turtle = canva.getContext("2d");
profils.addEventListener('mousemove', event => {
  mx = event.offsetX;
  my = event.offsetY;
});
imgYou.style.left = "0";
imgYou.style.top = `${r}px`;
imgYou.addEventListener('mousemove', e => {
  e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
  mx = e.offsetX + leftYou;
  my = e.offsetY + topYou;
});
game.players.forEach(player => {
  const name = player.name;
  if (name !== "you"){
    const a = 2*(Math.PI)*parseInt(name)/(game.players.length);
    pos.set(name, [-(Math.cos(a))*r, Math.sin(a)*r, true]);
  }
});
function checkTarget(name) {
  function checkT(){
    if (self.shotting && result.has(name) && (result.get(name))[2] !== 0){
      self.setOpeAtt(name);
      turnDone();
    } else {
      let posUser = pos.get(name);
      posUser[2] = !posUser[2];
      pos.set(name, posUser);
    }
  }
  return checkT;
}
for (let i=1; i<=number; i++) {
  let clone = imgMac.cloneNode(false);
  let [x, y, display] = pos.get(i.toString());
  clone.style.top = `${y+r}px`;
  clone.style.left = `${x+r}px`;
  clone.onclick = checkTarget(i.toString());
  clone.addEventListener('mousemove', e => {
    e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
    mx = x+r+64;
    my = y+r+64;
  });
  profils.appendChild(clone);
}
profils.removeChild(imgMac);
openOpe.onclick = () => {
  if (openOpe.checked) {
    pos.forEach((xy, name) => {
      const [x, y, display] = xy;
      pos.set(name,[x, y, true]);
    });
  } else {
    pos.forEach((xy, name) => {
      const [x, y, display] = xy;
      pos.set(name,[x, y, false]);
    });
  }
};

function arrow(xf, yf, xt, yt, s=30, d=Math.PI/6, n=1){
  const angle = Math.atan2(yt - yf, xt - xf);
  if (n === 1){
    turtle.moveTo(xf, yf);
    turtle.lineTo(xt, yt);
    turtle.stroke();
    turtle.closePath();
  } else if (n === 2){
    turtle.translate(xf, yf);
    turtle.rotate(angle-Math.PI/2);
    turtle.moveTo(-10, 0);
    turtle.lineTo(-10, Math.sqrt((xt-xf)**2+(yt-yf)**2)-10);
    turtle.moveTo(+10, 0);
    turtle.lineTo(+10, Math.sqrt((xt-xf)**2+(yt-yf)**2)-10);
    turtle.stroke();
    turtle.closePath();
    turtle.restore();
    turtle.save();
  }
  turtle.beginPath();
  turtle.moveTo(xt, yt);
  turtle.lineTo(xt- Math.cos(angle-d)*s,yt -Math.sin(angle-d)*s);
  turtle.lineTo(xt- Math.cos(angle+d)*s,yt -Math.sin(angle+d)*s);
  turtle.closePath();
  turtle.fill();
}
function oper(operation, x, y, ammo){
  turtle.save();
  const [ope, target, statu] = operation;
  if (ope === 0){
    ammo = ammo -1;
  } else if (ope === 1){
    if (statu === 1){
      turtle.strokeStyle = "green";
    }
    turtle.arc(x+64, y+64, 70, 0, Math.PI*2);
    turtle.stroke();
    turtle.closePath();
  } else if (ope < 2 || target === ""){
    turtle.closePath();
  } else {
    let xf = x+64, yf = y+100, xt, yt;
    if (target !== "you"){
      xt = (pos.get(target)[0])+60,yt = (pos.get(target)[1])+60;
    } else if (target === "you"){
      xt = -r+60, yt = 60;
    }
    if (ope === 2){
      turtle.strokeStyle = "yellow";
      turtle.fillStyle = "yellow";
      if (result.has(target)){
         const [ope1, target1, statu1] = result.get(target);
         if (ope1 === 1 && statu1 === 1){
           const angle = Math.atan2(yt - yf, xt - xf);
           xt = xt - 70*Math.cos(angle);
           yt = yt - 70*Math.sin(angle);
         }
      }
      arrow(xf, yf, xt, yt);
    } else if (ope === 3){
      arrow(xf, yf, xt, yt, s=40, d=Math.PI/3, n=2);      
    } else if (ope === 4){
      turtle.lineWidth = 10;
      turtle.strokeStyle = "purple";
      turtle.fillStyle = "purple";
      arrow(xf, yf, xt, yt, s=50, d=Math.PI/4);
    }
  }
  turtle.lineWidth = 5;
  turtle.strokeStyle = "black";
  turtle.fillStyle = "orange";
  let a = 20;
  for (let i=0;i<ammo;i++) {
    turtle.beginPath();
    turtle.arc(x+a, y+128, 20, 0, Math.PI*2);
    turtle.closePath();
    turtle.fill();
    turtle.stroke();
    a = a+50;
  }
  if (ope === 0){
    turtle.beginPath();
    if (statu === 1){
      turtle.strokeStyle = "blue";
    } else {
      turtle.strokeStyle = "red";
    }
    turtle.moveTo(x+a-20, y+128);
    turtle.lineTo(x+a+20, y+128);
    turtle.moveTo(x+a, y+108);
    turtle.lineTo(x+a, y+148);
    turtle.stroke();
    turtle.closePath();
  }
  turtle.restore();
}
function draw(){
  turtle.clearRect(0, 0, canva.width, canva.height);
  turtle.save();
  turtle.lineWidth = 5;
  turtle.strokeStyle = "red";
  turtle.fillStyle = "red";
  turtle.lineCap = "round";
  turtle.translate(r, r);
  turtle.beginPath();
  oper(result.get("you"), -r, 0, self.ammo);
  pos.forEach((xy, name)=> {
    const [x, y, display] = xy;
    if (result.has(name) && display){
      turtle.beginPath();
      let thisAmmo = 0;
      game.players.forEach(player => {
        if (player.name === name){
          thisAmmo = player.ammo;
        }
      });
      oper(result.get(name), x, y, thisAmmo);
    }
    if ((result.has(name)&&(result.get(name))[2] ===0)|| !result.has(name)){
      turtle.beginPath();
      turtle.moveTo(x+20, y+20);
      turtle.lineTo(x+110, y+110);
      turtle.moveTo(x+110, y+20);
      turtle.lineTo(x+20, y+110);
      turtle.stroke();
      turtle.closePath();
    }
  });
  turtle.restore();
  if (self.shotting){
    turtle.beginPath();
    arrow(64, 100+r, mx, my);
  }
  requestAnimationFrame(draw);
}

addAmmo.onclick = () => {self.setOpeAmmo();turnDone();};
defense.onclick = () => {self.setOpeDefe();turnDone();};
attack1.onclick = () => {self.setAtt(2);};
attack2.onclick = () => {self.setAtt(3);};
attack3.onclick = () => {self.setAtt(4);};
draw();